package team.snack.game;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.FontUIResource;

import team.snack.basis.Body;
import team.snack.basis.Food;
import team.snack.basis.Snake;
import team.snack.medium.Music;
import team.snack.net.Score;

/**
 * 游戏类
 *
 * @ClassName Game
 * @version 1.0
 * @Description [功能描述]
 * @author Java小队
 * @time 2019年1月15日 
 */
public class Game {
    public static Snake snake;
    public static Food food; 
    public static JFrame frame; 
    public static Draw draw; 
    public static boolean speedUp = false; 
    public static char dir = 'R'; 
    public static double speed = 12; 
    public static boolean win = false; 
    static boolean hard = false;
    static boolean wasd = false; 
    public static boolean music = true; 
    public static boolean invincible = false;
    static boolean pause = false; 
    static String configFilepath = "src/res/config.properties";
    static Color backColor = new Color(250, 249, 222);
    public static Music bg = new Music("bg"); 
    /** 蛇身减短的循环变量
     * 
     */
    int shorten = 0; 
    Music[] eat = {
            new Music("eat1"), new Music("eat2"), new Music("eat3")
        };
    Music fail = new Music("fail"); 
    Music eatFood; 
    char r = 'R';
    char l = 'L';
    char u = 'U';
    char d = 'D';

    public void go() { 
        initGlobalFontSetting();//初始化全局字体
        init();//初始化游戏
        keyListener();//添加键盘监听(包括方向，加速，暂停)

        while (true) {
            try {
                if (!pause) {
                    run();
                }
                int a = 5;
                // 只有在速度大于 5 时加速生效
                if (speedUp && (speed > a)) { 
                    Thread.sleep(4);
                } else {
                	// 否则延时默认秒数
                    Thread.sleep((int) speed); 
                }

                frame.requestFocusInWindow(); // 获取焦点
            } catch (Exception e) {
            }
        }
    }

    private void keyListener() {
    	frame.addKeyListener(new KeyListener() {
            @Override
            public void keyTyped(KeyEvent e) {
            }

            @Override
            public void keyReleased(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_SPACE) {
                    // 空格被松开,加速结束
                    speedUp = false;
                }
            }

            @Override
            public void keyPressed(KeyEvent e) {
            	boolean t1 = ((e.getKeyCode() == KeyEvent.VK_W) && wasd) ||
                        ((e.getKeyCode() == KeyEvent.VK_UP) && !wasd);
            	boolean t2 = ((e.getKeyCode() == KeyEvent.VK_S) && wasd) ||
                        ((e.getKeyCode() == KeyEvent.VK_DOWN) && !wasd);
            	boolean t3 = ((e.getKeyCode() == KeyEvent.VK_A) && wasd) ||
                        ((e.getKeyCode() == KeyEvent.VK_LEFT) && !wasd);
            	boolean t4 = ((e.getKeyCode() == KeyEvent.VK_D) && wasd) ||
                        ((e.getKeyCode() == KeyEvent.VK_RIGHT) && !wasd);
                if (t1) {
                    dir = u; 
                } else if (t2) {
                    dir = d; 
                } else if (t3) {
                    dir = l; 
                } else if (t4) {
                    dir = r; 
                }

                if (e.getKeyCode() == KeyEvent.VK_SPACE) {
                    // 按下空格,开始加速
                    speedUp = true;
                }

                if (e.getKeyCode() == KeyEvent.VK_P) {
                    pOrC(!pause);
                }
            }
        });
    }
    public void run() {
    	// 获取蛇头
        Body head = snake.getHead(); 
        
        if (dir == r) {
        	// 在右方添加新蛇头
            snake.addH(new Body(head.x + 1, head.y, head.color)); 
        } else if (dir == l) {
            snake.addH(new Body(head.x - 1, head.y, head.color)); 
        } else if (dir == u) {
            snake.addH(new Body(head.x, head.y - 1, head.color)); 
        } else if (dir == d) {
            snake.addH(new Body(head.x, head.y + 1, head.color)); 
        }

        snake.moveColor(); // 颜色依次上移

        // 获取新蛇头
        head = snake.getHead(); 

        /* 当蛇头坐标和食物坐标相差正负 20 时，吃掉食物 ；？否则缩短*/
        int r = 20;
        if ((Math.abs(food.x - head.x) < r) &&
                (Math.abs(food.y - head.y) < r)) {
        	// 随机抽取食物音乐的下标
        	double p = Math.random();
            int num = (int) (p * 3); 
            // 吃食物音效
            eatFood = eat[num]; 
            eatFood.play(); // 播放吃食物音效
            // 添加食物颜色的蛇尾
            snake.add(food.color); 
            food.newFood(); // 新建食物

            /* 以下是速度增快 */
            int a = 20,b = 6;
            if ((snake.getScore() < a) && (speed >= b)) {
                if (hard) {
                    speed -= 0.10;
                }

                speed -= 0.27;
                a = 50;b = 4;
            } else if ((snake.getScore() < a) && (speed >= b)) {
                if (hard) {
                    speed -= 0.10;
                }

                speed -= 0.10;
                a = 100;b = 2;
            } else if ((snake.getScore() < a) && (speed >= b)) {
                if (hard) {
                    speed -= 0.05;
                }

                speed -= 0.07;
            }

            /* 游戏胜利，达到 50 分 */
            a = 50;
            if ((snake.getScore() == a) && !win) {
                win = true;
            }
        } else {
            snake.remove(); // 移除尾巴？？？
        }

        boolean t1 = ((head.x < 0) || (head.y < 0) || (head.x > (draw.getWidth() - 20)) ||
                (head.y > draw.getHeight())) && !invincible;
        if (t1) {
//        	System.out.println(head.x);
//        	System.out.println(head.y);
            bg.stop();
            fail.play();
            JOptionPane.showMessageDialog(null, "很遗憾，你撞墙了");
            snake.newGame();//新游戏
        }

        boolean t2 = ((head.x < 0) || (head.y < 0) || (head.x > (draw.getWidth() - 20)) ||
                (head.y > draw.getHeight())) && invincible;
        if (t2) {
            if (head.x < 0) {
                head.x = draw.getWidth();
            }

            if (head.y < 0) {
                head.y = draw.getHeight();
            }

            if (head.x > draw.getWidth()) {
                head.x = 0;
            }

            if (head.y > draw.getHeight()) {
                head.y = 0;
            }
        }

        if ((shorten == 0) && !invincible && !pause) {
            snake.del();
        }

        /* 每50次蛇身缩短 */
        shorten++;
        
        int q = 50 ;
        if (shorten == q) {
            shorten = 0;
        }

        if (snake.empty()) {
            bg.stop();
            fail.play();
            JOptionPane.showMessageDialog(null, "很遗憾，你缩没了");
            snake.newGame();
        }

        frame.repaint();
    }

    /** 
     * @Description: 初始化游戏信息 
     */
    public void init() {
    	draw = new Draw(); 
        food = new Food(); 
        snake = new Snake(); 
        frame = new JFrame("五彩贪吃蛇"); 
        frame.setBounds(550, 260, 800, 500);
        // 设置窗口最小长宽
        frame.setMinimumSize(new Dimension(400, 300)); 
        frame.setBackground(new Color(250, 249, 222));
        frame.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosing(WindowEvent e) {
                    over();
                }
            });
        food.newFood(); // 生成食物
     // 把游戏面板添加到中间
        frame.add(BorderLayout.CENTER, draw); 
//        frame.requestFocusInWindow(); // 获取焦点

        File file = new File(configFilepath);

        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();//打印此可丢弃文件及其回溯到标准错误流
            }
        }

        /* 读取配置文件 */
        String a1 = "1";
        String a2 = "wasd";
        String a3 = "music";
        String a4 = "invincible";
        
        if (a1.equals(getProperties(a2))) {
            wasd = true;
        } else {
            wasd = false;
        }

        if (a1.equals(getProperties(a3))) {
            music = true;
        } else {
            music = false;
        }

        if (a1.equals(getProperties(a4))) {
            invincible = true;
        } else {
            invincible = false;
        }

        Color[] colors = {//背景颜色
                new Color(255, 255, 255), new Color(250, 249, 222),
                new Color(253, 230, 224), new Color(227, 237, 205),
                new Color(196, 237, 255), new Color(233, 235, 254),
                new Color(234, 234, 239)
            };

        try {
            backColor = colors[Integer.parseInt(getProperties("BackColor"))];
        } catch (Exception e) {
            backColor = new Color(250, 249, 222);
        }

        JMenuBar jmb = new JMenuBar();
        JMenu[] jm = new JMenu[3];

        /* 菜单栏 */
        // 按钮文字
        String[] menutext = { "游戏菜单", "游戏设置", "游戏帮助" }; 

        for (int i = 0; i < menutext.length; i++) {
            jm[i] = new JMenu(menutext[i]);
            jmb.add(jm[i]);
        }

        String[][] text = {
        		// 菜单文字
                { "新游戏", "排行榜", "退出游戏" }, 
                // 设置文字
                { "游戏难度", "声音设置", "按键模式", "游戏模式", "背景颜色" },
                // 帮助文字
                { "游戏规则", "联系作者" } 
            };

        JMenuItem[][] jmi = {
                new JMenuItem[text[0].length], new JMenu[text[1].length],
                new JMenuItem[text[2].length],
            };
        JMenuItem jmiName = new JMenuItem("玩家昵称");
        jmiName.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    pOrC(true);

                    String name = JOptionPane.showInputDialog(frame, "输入玩家昵称",
                            "设置玩家昵称", JOptionPane.QUESTION_MESSAGE);

                    if ((name == "") || (name == null)) {
                        name = "无名英雄";
                    }

                    writeproperties("Name", name);
                    pOrC(false);
                }
            });
        //玩家昵称
        jm[1].add(jmiName);

        for (int i = 0; i < jm.length; i++) {
            jm[i].addMenuListener(new MenuListener() {
                    @Override
                    public void menuSelected(MenuEvent e) {
                        pOrC(true);
                    }

                    @Override
                    public void menuDeselected(MenuEvent e) {
                        pOrC(false);
                    }

                    @Override
                    public void menuCanceled(MenuEvent e) {
                    }
                });

            for (int j = 0; j < jmi[i].length; j++) {
                if (i != 1) {
                    jmi[i][j] = new JMenuItem(text[i][j]);
                } else {
                    jmi[i][j] = new JMenu(text[i][j]);
                }

                jm[i].add(jmi[i][j]);
            }
        }

        for (int i = 0; i < jm.length; i++) {
            for (int j = 0; j < jmi[i].length; j++) {
                switch (i) {
                case 0: {
                    switch (j) {
                    case 0: {
                        /* 新游戏 */
                        jmi[i][j].addActionListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    snake.newGame();
                                    pOrC(false);
                                }
                            });

                        break;
                    }

                    case 1: {
                        /* 排行榜 */
                        jmi[i][j].addActionListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    pOrC(true);
                                    Score.show(frame);
                                    pOrC(false);
                                }
                            });

                        break;
                    }

                    case 2: {
                        /* 退出游戏 */
                        jmi[i][j].addActionListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    over();
                                }
                            });

                        break;
                    }
                    default:
                    	break;
                    }

                    break;
                }

                case 1: {
                	//setSelected():设置按钮的状态,如果选择按钮，则为 true，否则为false
                    switch (j) {
                    case 0: {
                        /* 游戏难度 */
                        JRadioButton r11; // 声明单选按钮

                        /* 游戏难度 */
                        JRadioButton r12; // 声明单选按钮
                        r11 = new JRadioButton("初级");
                        r11.setSelected(true);
                        r12 = new JRadioButton("高级");
                        r11.addActionListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    hard = false;
                                    r12.setSelected(false);
                                    r11.setSelected(true);
                                }
                            });
                        r12.addActionListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    hard = true;
                                    r11.setSelected(false);
                                    r12.setSelected(true);
                                }
                            });
                        // 将单选按钮添加到面板中
                        jmi[i][j].add(r11); 
                        jmi[i][j].add(r12);

                        break;
                    }

                    case 1: {
                        /* 声音设置 */
                        JRadioButton r21; // 声明单选按钮

                        /* 声音设置 */
                        JRadioButton r22; // 声明单选按钮
                        r21 = new JRadioButton("开");
                        r22 = new JRadioButton("关");

                        if (music == true) {
                            r21.setSelected(true);
                        } else {
                            r22.setSelected(true);
                        }

                        r21.addActionListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    music = true;
                                    bg.play();
                                    r22.setSelected(false);
                                    r21.setSelected(true);
                                }
                            });
                        r22.addActionListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    bg.stop();
                                    music = false;
                                    r21.setSelected(false);
                                    r22.setSelected(true);
                                }
                            });
                        // 将单选按钮添加到面板中
                        jmi[i][j].add(r21); 
                        jmi[i][j].add(r22);

                        break;
                    }

                    case 2: {
                        /* 按键模式 */
                        JRadioButton r31; // 声明单选按钮

                        /* 按键模式 */
                        JRadioButton r32; // 声明单选按钮
                        r31 = new JRadioButton("方向键");
                        r32 = new JRadioButton("WASD");

                        if (wasd == false) {
                            r31.setSelected(true);
                        } else {
                            r32.setSelected(true);
                        }

                        r31.addActionListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    wasd = false;
                                    r32.setSelected(false);
                                    r31.setSelected(true);
                                }
                            });
                        r32.addActionListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    wasd = true;
                                    r31.setSelected(false);
                                    r32.setSelected(true);
                                }
                            });
                        // 将单选按钮添加到面板中
                        jmi[i][j].add(r31); 
                        jmi[i][j].add(r32);

                        break;
                    }

                    case 3: {
                        /* 游戏模式 */
                        JRadioButton r41; // 声明单选按钮

                        /* 游戏模式 */
                        JRadioButton r42; // 声明单选按钮
                        r41 = new JRadioButton("普通模式");
                        r42 = new JRadioButton("无敌模式");

                        if (!invincible) {
                            r41.setSelected(true);
                        } else {
                            r42.setSelected(true);
                        }

                        r41.addActionListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    invincible = false;
                                    r42.setSelected(false);
                                    r41.setSelected(true);
                                }
                            });
                        r42.addActionListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    invincible = true;
                                    r41.setSelected(false);
                                    r42.setSelected(true);
                                }
                            });
                        // 将单选按钮添加到面板中
                        jmi[i][j].add(r41); 
                        jmi[i][j].add(r42);

                        break;
                    }

                    case 4: {
                        /* 背景颜色 */
                        String[] colortext = {
                                "银河白", "杏仁黄", "胭脂红", "青草绿", "海天蓝", "葛巾紫", "极光灰"
                            };
                        JRadioButton[] r5 = new JRadioButton[colortext.length];
                        // 初始化分组
                        ButtonGroup g = new ButtonGroup(); 

                        for (int k = 0; k < r5.length; k++) {
                            r5[k] = new JRadioButton(colortext[k]);
                            g.add(r5[k]);
                            jmi[i][j].add(r5[k]);

                            Color color = colors[k];
                            final int iColor = k;
                            r5[k].addActionListener(new ActionListener() {
                                    @Override
                                    public void actionPerformed(ActionEvent e) {
                                        backColor = color;
                                      //转换成 字符串
                                        writeproperties("BackColor",
                                            String.valueOf(iColor));
                                        draw.repaint();
                                    }
                                });
                        }

                        try {
                            r5[Integer.parseInt(getProperties("BackColor"))].setSelected(true);
                        } catch (Exception e) {
                            r5[2].setSelected(true);
                        }

                        break;
                    }
                    default:
                    	break;
                    }

                    break;
                }

                case 2: {
                    switch (j) {
                    case 0: {
                        jmi[i][j].addActionListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    pOrC(true);

                                    StringBuffer hintText = new StringBuffer();

                                    if (wasd) {
                                    	//append():将指定的字符串追加到此字符序列
                                        hintText.append("WASD 控制方向\n");
                                    } else {
                                        hintText.append("方向键控制方向\n");
                                    }

                                    hintText.append("空格 - 加速\np - 暂停\n");

                                    if (!Game.invincible) {
                                        hintText.append("Attantion\n蛇身会慢慢缩短\n请尽快捕食！");
                                    } else {
                                        hintText.append("可以穿墙");
                                    }

                                    JOptionPane.showMessageDialog(null,
                                        hintText.toString(), "游戏规则",
                                        JOptionPane.INFORMATION_MESSAGE);
                                    pOrC(false);
                                }
                            });

                        break;
                    }

                    case 1: {
                        jmi[i][j].addActionListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    pOrC(true);
                                    JOptionPane.showMessageDialog(null,
                                        "作者QQ：111111111\n请备注：贪吃蛇交流", "联系作者",
                                        JOptionPane.INFORMATION_MESSAGE);
                                    pOrC(false);
                                }
                            });

                        break;
                    }
                    default:
                    	break;
                    }

                    break;
                }
                default:
                	break;
                }
            }
        }

        snake.newGame();
        // 把菜单添加到上方
        frame.add(BorderLayout.NORTH, jmb); 
        // 显示窗口
        frame.setVisible(true); 
        bg.loop();
    }

    /**
     * @Description 设置全局字体
     */
    public static void initGlobalFontSetting() {
        Font font = new Font("Microsoft YaHei UI", Font.PLAIN, 14);
        FontUIResource fontRes = new FontUIResource(font);

        //for(@SuppressWarnings("rawtypes")Enumeration keys = UIManager.getDefaults().keys(); keys.hasMoreElements();){
        //告诉编译器忽略rawtypes警告信息
        for (Enumeration keys = UIManager.getDefaults().keys();
                keys.hasMoreElements();) {
            Object key = keys.nextElement();
            Object value = UIManager.get(key);

            if (value instanceof FontUIResource) {
                UIManager.put(key, fontRes);
            }
        }
    }

    /**
     * @Description 游戏暂停/继续
     * @param p
     */
    public void pOrC(boolean p) {
        pause = p;
    }

    /** 
     * @Description: 游戏结束
     */
    public void over() {
        frame.setVisible(false);

        if (wasd) {
            writeproperties("wasd", "1");
        } else {
            writeproperties("wasd", "0");
        }

        if (music) {
            writeproperties("music", "1");
        } else {
            writeproperties("music", "0");
        }

        if (invincible) {
            writeproperties("invincible", "1");
        } else {
            writeproperties("invincible", "0");
        }

        String name = "";
        name = getProperties("Name");

        if ((name == "") || (name == null)) {
            name = "无名英雄";
        }

        System.out.println("player: " + name);
        // 上传成绩至排行榜
        Score.upload(name, snake.getScore());
        // 窗口关闭的同时结束程序
        System.exit(0); 
    }

    /**
     * @Description 获取配置文件信息
     * @param key
     * @return String
     */
    public static String getProperties(String key) {
        Properties pps = new Properties();

        try {
            InputStream in = new BufferedInputStream(new FileInputStream(
                        configFilepath));
            pps.load(in);

            String value = pps.getProperty(key);
            System.out.println("[Config] Get: " + key + " = " + value);

            return value;
        } catch (IOException e) {
            e.printStackTrace();

            return null;
        }
    }

    /**
     * @Description 更新配置文件信息
     * @param key
     * @param value
     */
    public void writeproperties(String key, String value) {
        Properties pps = new Properties();

        try {
            InputStream in = new FileInputStream(configFilepath);
            //载入
            pps.load(in); 

            OutputStream out = new FileOutputStream(configFilepath);
            //调用Hashtable方法put,提供了getProperty方法的并行性。强制对属性键和值使用字符串。返回的值是要put的Hashtable调用的结果
            pps.setProperty(key, value);
          //将属性表中的属性列表(键和元素对)以适合使用load(InputStream)方法加载到属性表中的格式写入输出流
            pps.store(out, "Update " + key);
            System.out.println("[Config] Write: " + key + " = " + value);
        } catch (IOException e) {
            e.printStackTrace();

            return;
        }
    }

}
